﻿using System;
using System.Collections.Generic;
using UnityEngine;

public class RoadRing : MonoBehaviour
{
    public Material material;


    private void Start()
    {
        List<Vector3> Points = new List<Vector3>();
        for (int i = 0; i < transform.childCount; i++)
        {
            Points.Add(transform.GetChild(i).localPosition);
        }

        GetComponent<MeshFilter>().mesh = CreateRoad(Points.ToArray(), 4f);
        GetComponent<MeshRenderer>().material = material;
    }
    #region 创建路径 20200831
    /// <summary>创建路径Mesh</summary>
    /// <param name="trans"></param>
    /// <param name="width"></param>
    /// <returns></returns>
    private Mesh CreateRoad(Vector3[] trans, float width)
    {
        float Materials_Width = width;
        #region vertices
        List<Vector3> l_vertesx = new List<Vector3>();
        List<int> l_triangles = new List<int>();
        float uvi = 0;
        bool isLAngle = true;
        List<Vector2> l_uv = new List<Vector2>();
        Vector3[] _abcd = GetABCD(trans[0], trans[1], width / 2);
        l_vertesx.Add(_abcd[0]);
        l_vertesx.Add(_abcd[3]);
        l_uv.Add(new Vector2(0, 0));
        l_uv.Add(new Vector2(0, 1));

        Vector3 lastR = l_vertesx[0];
        Vector3 lastL = l_vertesx[1];

        Action<int, bool, bool> addTriangle = (index, currentL, isLastL) =>
        {
            l_triangles.Add((index - 1) * 4);
            l_triangles.Add((index - 1) * 4 + 2);
            l_triangles.Add((index - 1) * 4 + 1);

            l_triangles.Add((index - 1) * 4);
            l_triangles.Add((index - 1) * 4 + 1);
            l_triangles.Add((index - 1) * 4 + 2);


            if (currentL ^ isLAngle)
            {
                l_triangles.Add((index - 1) * 4 + 0);
                l_triangles.Add((index - 1) * 4 + 3);
                l_triangles.Add((index - 1) * 4 + 2);

                l_triangles.Add((index - 1) * 4 + 0);
                l_triangles.Add((index - 1) * 4 + 2);
                l_triangles.Add((index - 1) * 4 + 3);
            }
            else
            {
                l_triangles.Add((index - 1) * 4 + 1);
                l_triangles.Add((index - 1) * 4 + 3);
                l_triangles.Add((index - 1) * 4 + 2);

                l_triangles.Add((index - 1) * 4 + 1);
                l_triangles.Add((index - 1) * 4 + 2);
                l_triangles.Add((index - 1) * 4 + 3);
            }
        };

        for (int i = 1; i < trans.Length - 1; i++)
        {
            #region abcd-opmn
            Vector3[] abcd = GetABCD(trans[i - 1], trans[i], width / 2);
            Vector3 a = abcd[0];
            Vector3 b = abcd[1];
            Vector3 c = abcd[2];
            Vector3 d = abcd[3];

            Vector3[] opmn = GetABCD(trans[i + 1], trans[i], width / 2);
            Vector3 o = opmn[0];
            Vector3 p = opmn[1];
            Vector3 m = opmn[2];
            Vector3 n = opmn[3];


            SetGameObject(a, "A");
            SetGameObject(b, "B");
            SetGameObject(c, "C");
            SetGameObject(d, "D");
            
            SetGameObject(o, "o");
            SetGameObject(p, "p");
            SetGameObject(m, "m");
            SetGameObject(n, "n");


            #endregion
            Vector3 cl3 = trans[i] - trans[i - 1];
            Vector3 nc3 = trans[i + 1] - trans[i];
            Vector3 crossRes = Vector3.Cross(cl3, nc3);

            if (crossRes.y < 0)
            {
                Debug.Log("左角" + i);
                Vector3 k = GetLinesIntersection(a, b, m, n) + new Vector3(0, trans[i].y, 0);//k点与i点等高
                Vector3 l = GetLinesIntersection(c, d, o, p) + new Vector3(0, trans[i].y, 0);//l点与i点等高

                Vector3 e = ParallelLinePoint(d, l, -width, l);
                Vector3 r = ParallelLinePoint(o, l, width, l);

                SetGameObject(k, "k");
                SetGameObject(l, "l");
                SetGameObject(e, "e");
                SetGameObject(r, "r");

                float kr = Vector3.Distance(k, r);
                float ke = Vector3.Distance(k, e);

                float rn = Vector3.Distance(r, n);
                float ea = Vector3.Distance(e, a);

                float ry = (rn * k.y + kr * n.y) / (kr + rn);
                float ey = (ea * k.y + ke * a.y) / (ke + ea);

                r = new Vector3(r.x, ry, r.z);
                e = new Vector3(e.x, ey, e.z);

                l_vertesx.Add(l);
                l_vertesx.Add(k);

                l_vertesx.Add(l);
                l_vertesx.Add(k);

                float ll = Vector3.Distance(lastL, l);
                float ek = Vector3.Distance(k, e);

                l_uv.Add(new Vector2((uvi + ll) / Materials_Width, 1));//3 l
                l_uv.Add(new Vector2((uvi + ek + ll) / Materials_Width, 0));//4 k

                l_uv.Add(new Vector2((uvi + ek + ll + kr) / Materials_Width, 1));//5 l
                l_uv.Add(new Vector2((uvi + ek + ll) / Materials_Width, 0));//7 k

                if (i != 0)
                    addTriangle(i, false, isLAngle);

                uvi = uvi + (ek + ll + kr);
                lastL = l;
                lastR = r;
                isLAngle = false;
            }
            else
            {
                Debug.Log("右角" + i);
                Vector3 k = GetLinesIntersection(c, d, o, p) + new Vector3(0, trans[i].y, 0);
                Vector3 l = GetLinesIntersection(a, b, m, n) + new Vector3(0, trans[i].y, 0);

                Vector3 e = ParallelLinePoint(a, l, width, l);
                Vector3 r = ParallelLinePoint(n, l, -width, l);

                SetGameObject(k, "k");
                SetGameObject(l, "l");
                SetGameObject(e, "e");
                SetGameObject(r, "r");

                float kr = Vector3.Distance(k, r);
                float ke = Vector3.Distance(k, e);

                float ro = Vector3.Distance(r, o);
                float ed = Vector3.Distance(e, d);

                float ry = (ro * k.y + kr * n.y) / (kr + ro);
                float ey = (ed * k.y + ke * a.y) / (ke + ed);

                r = new Vector3(r.x, ry, r.z);
                e = new Vector3(e.x, ey, e.z);

                l_vertesx.Add(l);
                l_vertesx.Add(k);

                l_vertesx.Add(l);
                l_vertesx.Add(k);

                float ll = Vector3.Distance(lastR, l);
                float ek = Vector3.Distance(k, e);

                l_uv.Add(new Vector2((uvi + ll) / Materials_Width, 0));
                l_uv.Add(new Vector2((uvi + ek + ll) / Materials_Width, 1));

                l_uv.Add(new Vector2((uvi + ek + ll + kr) / Materials_Width, 0));
                l_uv.Add(new Vector2((uvi + ek + ll) / Materials_Width, 1));

                if (i != 0)
                    addTriangle(i, true, isLAngle);

                uvi = uvi + (ek + ll + kr);
                lastL = e;
                lastR = r;
                isLAngle = true;
            }
        }

        Vector3[] _opmn = GetABCD(trans[trans.Length - 2], trans[trans.Length - 1], width / 2);
        l_vertesx.Add(_opmn[2]);
        l_vertesx.Add(_opmn[1]);

        //最后角按照左角计算
        addTriangle(trans.Length - 1, false, isLAngle);

        float lr = Vector3.Distance(l_vertesx[l_vertesx.Count - 2], lastL);

        l_uv.Add(new Vector2((uvi + lr) / Materials_Width, 1));
        l_uv.Add(new Vector2((uvi + lr) / Materials_Width, 0));
        #endregion

        Vector3[] normals = new Vector3[l_vertesx.Count];
        for (int i = 0; i < normals.Length; i++)
        {
            normals[i] = Vector3.up;
        }

        Mesh mesh = new Mesh();
        mesh.vertices = l_vertesx.ToArray();
        mesh.triangles = l_triangles.ToArray();
        mesh.normals = normals;
        mesh.uv = l_uv.ToArray();
        return mesh;
    }

    private Vector3[] GetABCD(Vector3 O1, Vector3 O2, float am)
    {
        Vector3 A = ParallelLinePoint(O1, O2, -am, O1);
        Vector3 B = ParallelLinePoint(O1, O2, -am, O2);
        Vector3 C = ParallelLinePoint(O1, O2, am, O2);
        Vector3 D = ParallelLinePoint(O1, O2, am, O1);

        Vector3[] abcd = new Vector3[] { A, B, C, D };
        return abcd;
    }
    public GameObject _game;
    private void SetGameObject(Vector3 pos, string _name)
    {
        GameObject game = Instantiate(_game);
        game.name = _name;
        game.transform.parent = transform;
        game.transform.localPosition = pos;
        game.transform.localScale = Vector3.one * 0.01f;
    }

    #region 核心
    /// <summary>求平行线上的点</summary>
    /// <param name="m">m点</param>
    /// <param name="n">n点</param>
    /// <param name="am">距离</param>
    /// <param name="origin">原点</param>
    /// <returns></returns>
    private Vector3 ParallelLinePoint(Vector3 m, Vector3 n, float am, Vector3 origin)
    {
        float mn = Vector3.Distance(n, m);
        float mo = n.x - m.x;
        float no = n.z - m.z;
        float ox = origin.x;
        float oy = origin.z;

        float ap = am * mo / mn;
        float pm = am * no / mn;
        return new Vector3(ox - pm, origin.y, ap + oy);
    }
    /// <summary>求相交线的相交点</summary>
    /// <param name="o1"></param>
    /// <param name="o2"></param>
    /// <param name="o3"></param>
    /// <param name="o4"></param>
    private Vector3 GetLinesIntersection(Vector3 o1, Vector3 o2, Vector3 o3, Vector3 o4)
    {
        float x1 = o1.x;
        float x2 = o2.x;
        float x3 = o3.x;
        float x4 = o4.x;

        //float y1 = o1.y;
        //float y2 = o2.y;
        //float y3 = o3.y;
        //float y4 = o4.y;

        float z1 = o1.z;
        float z2 = o2.z;
        float z3 = o3.z;
        float z4 = o4.z;

        float x = (x1 * (z2 - z1) * (x4 - x3) - x3 * (z4 - z3) * (x2 - x1) + (z3 - z1) * (x4 - x3) * (x2 - x1)) / ((z2 - z1) * (x4 - x3) - (z4 - z3) * (x2 - x1));
        float y = 0;// (y1 * (x2 - x1) * (y4 - y3) - y3 * (x4 - x3) * (y2 - y1) + (x3 - x1) * (y4 - y3) * (y2 - y1)) / ((x2 - x1) * (y4 - y3) - (x4 - x3) * (y2 - y1));
        float z = (z1 * (x2 - x1) * (z4 - z3) - z3 * (x4 - x3) * (z2 - z1) + (x3 - x1) * (z4 - z3) * (z2 - z1)) / ((x2 - x1) * (z4 - z3) - (x4 - x3) * (z2 - z1));

        return new Vector3(x, y, z);
    }
    #endregion
    #endregion
}
